﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Bluegrams.Calcex;

namespace Parabola
{
    public partial class BlueGraphBox : UserControl
    {

        double rightshift, upshift;
        private List<MathFunction> functions;
        public string[] Functions
        {
            get
            {
                string[] outarr = new string[functions.Count];
                for (int i = 0; i < outarr.Length; i++)
                    outarr[i] = functions[i].Term;
                return outarr;
            }
        }

        #region Private Properties
        private int XAxis
        {
            get { return this.Height / 2 + (int)(upshift * 30); }
        }
        private int YAxis
        {
            get { return this.Width / 2 - (int)(rightshift * 30); }
        }

        private int XLeftBorder
        {
            get { return Convert.ToInt32(Math.Floor((double)(0 - YAxis) / 30)); }
        }
        private int XRightBorder
        {
            get { return Convert.ToInt32(Math.Ceiling((double)(this.Width - YAxis) / 30)); }
        }
        private int YUpperBorder
        {
            get { return Convert.ToInt32(Math.Floor((double)(this.Height - XAxis) / 30 * -1)); }
        }
        #endregion

        #region Public methods

        public BlueGraphBox()
        {
            functions = new List<MathFunction>();
            rightshift = 0;
            upshift = 0;
            InitializeComponent();
            panFocus.Focus();
        }

        public bool AddGraph(string name, string term, Color col)
        {
            MathParser validparser = new MathParser("x");
            if (!validparser.IsValid(term))
                return false;
            MathFunction func = new MathFunction(name, term, col);
            functions.Add(func);
            this.Invalidate();
            return true;
        }

        public void Clear()
        {
            functions.Clear();
            this.Invalidate();
        }

        public void RemoveGraph(int pos)
        {
            functions.RemoveAt(pos);
            this.Invalidate();
        }

        public void SetCenter(Point newcenter)
        {
            upshift = newcenter.Y;
            rightshift = newcenter.X;
            this.Invalidate();
        }

        public new void DrawToBitmap(Bitmap bitmap, Rectangle rectangle)
        {
            DrawToBitmap(bitmap, rectangle, true);
        }

        public void DrawToBitmap(Bitmap bitmap, Rectangle rectangle, bool explain)
        {
            using (Graphics g = Graphics.FromImage(bitmap))
            using (Font f = new Font("Calibri", 10))
            {
                PaintEventArgs pe = new PaintEventArgs(g, rectangle);
                plotbox_PaintGrid(pe);
                plotbox_PaintExtras(pe);
                plotbox_PaintGraphs(pe);
                if (explain)
                {
                    for (int i = 0; i < functions.Count; i++)
                        g.DrawString(String.Format("{0}(x) = {1}", functions[i].Name, functions[i].Term), f,
                            new SolidBrush(functions[i].GraphColor), new Point(10, 10 + i * 15));
                }
            }
        }

        #endregion

        #region Private methods

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            plotbox_PaintGrid(e);
            plotbox_PaintExtras(e);
            plotbox_PaintGraphs(e);
        }

        private void move_Diagram(object sender, EventArgs e)
        {
            if (sender == butLeft) rightshift--;
            else if (sender == butRight) rightshift++;
            else if (sender == butDown) upshift--;
            else if (sender == butUp) upshift++;
            this.Invalidate();
            panFocus.Focus();
        }

        private void plotbox_Resize(object sender, EventArgs e)
        {
            butUp.Location = new Point(this.Width / 2 - 25, butUp.Location.Y);
            butDown.Location = new Point(this.Width / 2 - 25, this.Height - 25);
            butLeft.Location = new Point(0, this.Height / 2 - 25);
            butRight.Location = new Point(this.Width - 25, this.Height / 2 - 25);
            this.Invalidate();
        }

        private void plotbox_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
        {
            if (e.KeyCode == Keys.Left)
            {
                butLeft.PerformClick();
            }
            else if (e.KeyCode == Keys.Right)
            {
                butRight.PerformClick();
            }
            else if (e.KeyCode == Keys.Up)
            {
                butUp.PerformClick();
            }
            else if (e.KeyCode == Keys.Down)
            {
                butDown.PerformClick();
            }
        }

        private void but_Enter(object sender, EventArgs e)
        {
            panFocus.Focus(); 
        }

        private void plotbox_Click(object sender, EventArgs e)
        {
            panFocus.Focus();
        }

        private void plotbox_Enter(object sender, EventArgs e)
        {
            this.BorderStyle = BorderStyle.FixedSingle;
        }

        private void plotbox_Leave(object sender, EventArgs e)
        {
            this.BorderStyle = BorderStyle.None;
        }

        private void plotbox_PaintGraphs(PaintEventArgs e)
        {
            foreach (MathFunction func in functions)
            {
                using (Pen graphpen = new Pen(new SolidBrush(func.GraphColor), 2))
                {
                    Dictionary<double, double> values = func.GetData(XLeftBorder, XRightBorder);
                    double[] x_vals = values.Keys.ToArray();
                    double[] y_vals = values.Values.ToArray();
                    for (int i = 0; i < values.Count - 1; i++)
                    {
                        if (!double.IsNaN(y_vals[i]) && !double.IsNaN(y_vals[i + 1]))
                        {
                            Point spoint = toPoint(x_vals[i], y_vals[i]);
                            Point epoint = toPoint(x_vals[i + 1], Convert.ToDouble(y_vals[i + 1]));
                            if (spoint.Y < this.Height && spoint.Y > 0 || epoint.Y < this.Height && epoint.Y > 0)
                            {
                                e.Graphics.DrawLine(graphpen, spoint, epoint);
                            }
                            else if (YUpperBorder > spoint.Y && YUpperBorder < epoint.Y || YUpperBorder < spoint.Y && YUpperBorder > epoint.Y)
                            {
                                //not implemented
                            }
                        }
                    }
                }
            }
        }

        private Point toPoint(double xvalue, double yvalue)
        {
            return new Point((int)(YAxis + xvalue * 30), (int)(XAxis - yvalue * 30));
        }

        private void plotbox_PaintExtras(PaintEventArgs e)
        {
            if (XAxis >= 0 && XAxis <= this.Width || YAxis >= 0 && YAxis <= this.Height)
            {
                Point[] xangle = new Point[] {new Point(this.Width-37,XAxis-6),new Point(this.Width-37,XAxis+6),
                new Point(this.Width-25, XAxis)};
                Point[] yangle = new Point[] { new Point(YAxis, 25), new Point(YAxis - 6, 37), new Point(YAxis + 6, 37) };
                using (SolidBrush brush = new SolidBrush(Color.Black))
                {
                    e.Graphics.FillPolygon(brush, xangle);
                    e.Graphics.FillPolygon(brush, yangle);
                }
            }
        }

        private void plotbox_PaintGrid(PaintEventArgs e)
        {
            // draw x-lines right
            int rightwards = YAxis + 30, i = 1;
            while (rightwards <= this.Width)
            {
                gridline(e.Graphics, rightwards, 0, rightwards, this.Height, Convert.ToString(i), rightwards, XAxis + 2);
                i++;
                rightwards += 30;
            }
            // draw x-lines left
            int leftwards = YAxis - 30, q = -1;
            while (leftwards >= 0)
            {
                gridline(e.Graphics, leftwards, 0, leftwards, this.Height, Convert.ToString(q), leftwards, XAxis + 2);
                q--;
                leftwards -= 30;
            }
            // draw y-lines down
            int downwards = XAxis + 30, p = -1;
            while (downwards <= this.Height)
            {
                gridline(e.Graphics, 0, downwards, this.Width, downwards, Convert.ToString(p), YAxis - 10, downwards - 7);
                p--;
                downwards += 30;
            }
            // draw y-lines up
            int upwards = XAxis - 30, j = 1;
            while (upwards >= 0)
            {
                gridline(e.Graphics, 0, upwards, this.Width, upwards, Convert.ToString(j), YAxis - 10, upwards - 7);
                j++;
                upwards -= 30;
            }
            using (Pen axpen = new Pen(new SolidBrush(Color.Black), 2))
            {
                // draw axes
                e.Graphics.DrawLine(axpen, YAxis, 0, YAxis, this.Height);
                e.Graphics.DrawLine(axpen, 0, XAxis, this.Width, XAxis);
            }
        }

        private void gridline(Graphics g, int x1, int y1, int x2, int y2, string num, int numx, int numy)
        {
            using (Pen linepen = new Pen(new SolidBrush(Color.LightGray), 1))
            using (Font font = new Font("Calibri", 10))
            using (SolidBrush brush = new SolidBrush(Color.Black))
            {
                g.DrawLine(linepen, x1, y1, x2, y2);
                int wl = num.Length;
                if (num != "0")
                {
                    g.DrawString(num, font, brush, numx - wl * 5, numy);
                }
            }
        }

        #endregion
    }
}
